/*!
 * # Semantic UI - Shape
 * http://github.com/semantic-org/semantic-ui/
 *
 *
 * Released under the MIT license
 * http://opensource.org/licenses/MIT
 *
 */

(function($, window, document, undefined) {
  "use strict";

  window =
    typeof window != "undefined" && window.Math == Math
      ? window
      : typeof self != "undefined" && self.Math == Math
        ? self
        : Function("return this")();

  $.fn.shape = function(parameters) {
    var $allModules = $(this),
      $body = $("body"),
      time = new Date().getTime(),
      performance = [],
      query = arguments[0],
      methodInvoked = typeof query == "string",
      queryArguments = [].slice.call(arguments, 1),
      requestAnimationFrame =
        window.requestAnimationFrame ||
        window.mozRequestAnimationFrame ||
        window.webkitRequestAnimationFrame ||
        window.msRequestAnimationFrame ||
        function(callback) {
          setTimeout(callback, 0);
        },
      returnedValue;

    $allModules.each(function() {
      var moduleSelector = $allModules.selector || "",
        settings = $.isPlainObject(parameters)
          ? $.extend(true, {}, $.fn.shape.settings, parameters)
          : $.extend({}, $.fn.shape.settings),
        // internal aliases
        namespace = settings.namespace,
        selector = settings.selector,
        error = settings.error,
        className = settings.className,
        // define namespaces for modules
        eventNamespace = "." + namespace,
        moduleNamespace = "module-" + namespace,
        // selector cache
        $module = $(this),
        $sides = $module.find(selector.sides),
        $side = $module.find(selector.side),
        // private variables
        nextIndex = false,
        $activeSide,
        $nextSide,
        // standard module
        element = this,
        instance = $module.data(moduleNamespace),
        module;

      module = {
        initialize: function() {
          module.verbose("Initializing module for", element);
          module.set.defaultSide();
          module.instantiate();
        },

        instantiate: function() {
          module.verbose("Storing instance of module", module);
          instance = module;
          $module.data(moduleNamespace, instance);
        },

        destroy: function() {
          module.verbose("Destroying previous module for", element);
          $module.removeData(moduleNamespace).off(eventNamespace);
        },

        refresh: function() {
          module.verbose("Refreshing selector cache for", element);
          $module = $(element);
          $sides = $(this).find(selector.shape);
          $side = $(this).find(selector.side);
        },

        repaint: function() {
          module.verbose("Forcing repaint event");
          var shape = $sides[0] || document.createElement("div"),
            fakeAssignment = shape.offsetWidth;
        },

        animate: function(propertyObject, callback) {
          module.verbose("Animating box with properties", propertyObject);
          callback =
            callback ||
            function(event) {
              module.verbose("Executing animation callback");
              if (event !== undefined) {
                event.stopPropagation();
              }
              module.reset();
              module.set.active();
            };
          settings.beforeChange.call($nextSide[0]);
          if (module.get.transitionEvent()) {
            module.verbose("Starting CSS animation");
            $module.addClass(className.animating);
            $sides
              .css(propertyObject)
              .one(module.get.transitionEvent(), callback);
            module.set.duration(settings.duration);
            requestAnimationFrame(function() {
              $module.addClass(className.animating);
              $activeSide.addClass(className.hidden);
            });
          } else {
            callback();
          }
        },

        queue: function(method) {
          module.debug("Queueing animation of", method);
          $sides.one(module.get.transitionEvent(), function() {
            module.debug("Executing queued animation");
            setTimeout(function() {
              $module.shape(method);
            }, 0);
          });
        },

        reset: function() {
          module.verbose("Animating states reset");
          $module
            .removeClass(className.animating)
            .attr("style", "")
            .removeAttr("style");
          // removeAttr style does not consistently work in safari
          $sides.attr("style", "").removeAttr("style");
          $side
            .attr("style", "")
            .removeAttr("style")
            .removeClass(className.hidden);
          $nextSide
            .removeClass(className.animating)
            .attr("style", "")
            .removeAttr("style");
        },

        is: {
          complete: function() {
            return $side.filter("." + className.active)[0] == $nextSide[0];
          },
          animating: function() {
            return $module.hasClass(className.animating);
          }
        },

        set: {
          defaultSide: function() {
            $activeSide = $module.find("." + settings.className.active);
            $nextSide =
              $activeSide.next(selector.side).length > 0
                ? $activeSide.next(selector.side)
                : $module.find(selector.side).first();
            nextIndex = false;
            module.verbose("Active side set to", $activeSide);
            module.verbose("Next side set to", $nextSide);
          },

          duration: function(duration) {
            duration = duration || settings.duration;
            duration = typeof duration == "number" ? duration + "ms" : duration;
            module.verbose("Setting animation duration", duration);
            if (settings.duration || settings.duration === 0) {
              $sides.add($side).css({
                "-webkit-transition-duration": duration,
                "-moz-transition-duration": duration,
                "-ms-transition-duration": duration,
                "-o-transition-duration": duration,
                "transition-duration": duration
              });
            }
          },

          currentStageSize: function() {
            var $activeSide = $module.find("." + settings.className.active),
              width = $activeSide.outerWidth(true),
              height = $activeSide.outerHeight(true);
            $module.css({
              width: width,
              height: height
            });
          },

          stageSize: function() {
            var $clone = $module.clone().addClass(className.loading),
              $activeSide = $clone.find("." + settings.className.active),
              $nextSide = nextIndex
                ? $clone.find(selector.side).eq(nextIndex)
                : $activeSide.next(selector.side).length > 0
                  ? $activeSide.next(selector.side)
                  : $clone.find(selector.side).first(),
              newWidth =
                settings.width == "next"
                  ? $nextSide.outerWidth(true)
                  : settings.width == "initial"
                    ? $module.width()
                    : settings.width,
              newHeight =
                settings.height == "next"
                  ? $nextSide.outerHeight(true)
                  : settings.height == "initial"
                    ? $module.height()
                    : settings.height;
            $activeSide.removeClass(className.active);
            $nextSide.addClass(className.active);
            $clone.insertAfter($module);
            $clone.remove();
            if (settings.width != "auto") {
              $module.css("width", newWidth + settings.jitter);
              module.verbose("Specifying width during animation", newWidth);
            }
            if (settings.height != "auto") {
              $module.css("height", newHeight + settings.jitter);
              module.verbose("Specifying height during animation", newHeight);
            }
          },

          nextSide: function(selector) {
            nextIndex = selector;
            $nextSide = $side.filter(selector);
            nextIndex = $side.index($nextSide);
            if ($nextSide.length === 0) {
              module.set.defaultSide();
              module.error(error.side);
            }
            module.verbose("Next side manually set to", $nextSide);
          },

          active: function() {
            module.verbose("Setting new side to active", $nextSide);
            $side.removeClass(className.active);
            $nextSide.addClass(className.active);
            settings.onChange.call($nextSide[0]);
            module.set.defaultSide();
          }
        },

        flip: {
          up: function() {
            if (
              module.is.complete() &&
              !module.is.animating() &&
              !settings.allowRepeats
            ) {
              module.debug("Side already visible", $nextSide);
              return;
            }
            if (!module.is.animating()) {
              module.debug("Flipping up", $nextSide);
              var transform = module.get.transform.up();
              module.set.stageSize();
              module.stage.above();
              module.animate(transform);
            } else {
              module.queue("flip up");
            }
          },

          down: function() {
            if (
              module.is.complete() &&
              !module.is.animating() &&
              !settings.allowRepeats
            ) {
              module.debug("Side already visible", $nextSide);
              return;
            }
            if (!module.is.animating()) {
              module.debug("Flipping down", $nextSide);
              var transform = module.get.transform.down();
              module.set.stageSize();
              module.stage.below();
              module.animate(transform);
            } else {
              module.queue("flip down");
            }
          },

          left: function() {
            if (
              module.is.complete() &&
              !module.is.animating() &&
              !settings.allowRepeats
            ) {
              module.debug("Side already visible", $nextSide);
              return;
            }
            if (!module.is.animating()) {
              module.debug("Flipping left", $nextSide);
              var transform = module.get.transform.left();
              module.set.stageSize();
              module.stage.left();
              module.animate(transform);
            } else {
              module.queue("flip left");
            }
          },

          right: function() {
            if (
              module.is.complete() &&
              !module.is.animating() &&
              !settings.allowRepeats
            ) {
              module.debug("Side already visible", $nextSide);
              return;
            }
            if (!module.is.animating()) {
              module.debug("Flipping right", $nextSide);
              var transform = module.get.transform.right();
              module.set.stageSize();
              module.stage.right();
              module.animate(transform);
            } else {
              module.queue("flip right");
            }
          },

          over: function() {
            if (
              module.is.complete() &&
              !module.is.animating() &&
              !settings.allowRepeats
            ) {
              module.debug("Side already visible", $nextSide);
              return;
            }
            if (!module.is.animating()) {
              module.debug("Flipping over", $nextSide);
              module.set.stageSize();
              module.stage.behind();
              module.animate(module.get.transform.over());
            } else {
              module.queue("flip over");
            }
          },

          back: function() {
            if (
              module.is.complete() &&
              !module.is.animating() &&
              !settings.allowRepeats
            ) {
              module.debug("Side already visible", $nextSide);
              return;
            }
            if (!module.is.animating()) {
              module.debug("Flipping back", $nextSide);
              module.set.stageSize();
              module.stage.behind();
              module.animate(module.get.transform.back());
            } else {
              module.queue("flip back");
            }
          }
        },

        get: {
          transform: {
            up: function() {
              var translate = {
                y: -(
                  ($activeSide.outerHeight(true) -
                    $nextSide.outerHeight(true)) /
                  2
                ),
                z: -($activeSide.outerHeight(true) / 2)
              };
              return {
                transform:
                  "translateY(" +
                  translate.y +
                  "px) translateZ(" +
                  translate.z +
                  "px) rotateX(-90deg)"
              };
            },

            down: function() {
              var translate = {
                y: -(
                  ($activeSide.outerHeight(true) -
                    $nextSide.outerHeight(true)) /
                  2
                ),
                z: -($activeSide.outerHeight(true) / 2)
              };
              return {
                transform:
                  "translateY(" +
                  translate.y +
                  "px) translateZ(" +
                  translate.z +
                  "px) rotateX(90deg)"
              };
            },

            left: function() {
              var translate = {
                x: -(
                  ($activeSide.outerWidth(true) - $nextSide.outerWidth(true)) /
                  2
                ),
                z: -($activeSide.outerWidth(true) / 2)
              };
              return {
                transform:
                  "translateX(" +
                  translate.x +
                  "px) translateZ(" +
                  translate.z +
                  "px) rotateY(90deg)"
              };
            },

            right: function() {
              var translate = {
                x: -(
                  ($activeSide.outerWidth(true) - $nextSide.outerWidth(true)) /
                  2
                ),
                z: -($activeSide.outerWidth(true) / 2)
              };
              return {
                transform:
                  "translateX(" +
                  translate.x +
                  "px) translateZ(" +
                  translate.z +
                  "px) rotateY(-90deg)"
              };
            },

            over: function() {
              var translate = {
                x: -(
                  ($activeSide.outerWidth(true) - $nextSide.outerWidth(true)) /
                  2
                )
              };
              return {
                transform: "translateX(" + translate.x + "px) rotateY(180deg)"
              };
            },

            back: function() {
              var translate = {
                x: -(
                  ($activeSide.outerWidth(true) - $nextSide.outerWidth(true)) /
                  2
                )
              };
              return {
                transform: "translateX(" + translate.x + "px) rotateY(-180deg)"
              };
            }
          },

          transitionEvent: function() {
            var element = document.createElement("element"),
              transitions = {
                transition: "transitionend",
                OTransition: "oTransitionEnd",
                MozTransition: "transitionend",
                WebkitTransition: "webkitTransitionEnd"
              },
              transition;
            for (transition in transitions) {
              if (element.style[transition] !== undefined) {
                return transitions[transition];
              }
            }
          },

          nextSide: function() {
            return $activeSide.next(selector.side).length > 0
              ? $activeSide.next(selector.side)
              : $module.find(selector.side).first();
          }
        },

        stage: {
          above: function() {
            var box = {
              origin:
                ($activeSide.outerHeight(true) - $nextSide.outerHeight(true)) /
                2,
              depth: {
                active: $nextSide.outerHeight(true) / 2,
                next: $activeSide.outerHeight(true) / 2
              }
            };
            module.verbose(
              "Setting the initial animation position as above",
              $nextSide,
              box
            );
            $sides.css({
              transform: "translateZ(-" + box.depth.active + "px)"
            });
            $activeSide.css({
              transform: "rotateY(0deg) translateZ(" + box.depth.active + "px)"
            });
            $nextSide.addClass(className.animating).css({
              top: box.origin + "px",
              transform: "rotateX(90deg) translateZ(" + box.depth.next + "px)"
            });
          },

          below: function() {
            var box = {
              origin:
                ($activeSide.outerHeight(true) - $nextSide.outerHeight(true)) /
                2,
              depth: {
                active: $nextSide.outerHeight(true) / 2,
                next: $activeSide.outerHeight(true) / 2
              }
            };
            module.verbose(
              "Setting the initial animation position as below",
              $nextSide,
              box
            );
            $sides.css({
              transform: "translateZ(-" + box.depth.active + "px)"
            });
            $activeSide.css({
              transform: "rotateY(0deg) translateZ(" + box.depth.active + "px)"
            });
            $nextSide.addClass(className.animating).css({
              top: box.origin + "px",
              transform: "rotateX(-90deg) translateZ(" + box.depth.next + "px)"
            });
          },

          left: function() {
            var height = {
                active: $activeSide.outerWidth(true),
                next: $nextSide.outerWidth(true)
              },
              box = {
                origin: (height.active - height.next) / 2,
                depth: {
                  active: height.next / 2,
                  next: height.active / 2
                }
              };
            module.verbose(
              "Setting the initial animation position as left",
              $nextSide,
              box
            );
            $sides.css({
              transform: "translateZ(-" + box.depth.active + "px)"
            });
            $activeSide.css({
              transform: "rotateY(0deg) translateZ(" + box.depth.active + "px)"
            });
            $nextSide.addClass(className.animating).css({
              left: box.origin + "px",
              transform: "rotateY(-90deg) translateZ(" + box.depth.next + "px)"
            });
          },

          right: function() {
            var height = {
                active: $activeSide.outerWidth(true),
                next: $nextSide.outerWidth(true)
              },
              box = {
                origin: (height.active - height.next) / 2,
                depth: {
                  active: height.next / 2,
                  next: height.active / 2
                }
              };
            module.verbose(
              "Setting the initial animation position as left",
              $nextSide,
              box
            );
            $sides.css({
              transform: "translateZ(-" + box.depth.active + "px)"
            });
            $activeSide.css({
              transform: "rotateY(0deg) translateZ(" + box.depth.active + "px)"
            });
            $nextSide.addClass(className.animating).css({
              left: box.origin + "px",
              transform: "rotateY(90deg) translateZ(" + box.depth.next + "px)"
            });
          },

          behind: function() {
            var height = {
                active: $activeSide.outerWidth(true),
                next: $nextSide.outerWidth(true)
              },
              box = {
                origin: (height.active - height.next) / 2,
                depth: {
                  active: height.next / 2,
                  next: height.active / 2
                }
              };
            module.verbose(
              "Setting the initial animation position as behind",
              $nextSide,
              box
            );
            $activeSide.css({
              transform: "rotateY(0deg)"
            });
            $nextSide.addClass(className.animating).css({
              left: box.origin + "px",
              transform: "rotateY(-180deg)"
            });
          }
        },
        setting: function(name, value) {
          module.debug("Changing setting", name, value);
          if ($.isPlainObject(name)) {
            $.extend(true, settings, name);
          } else if (value !== undefined) {
            if ($.isPlainObject(settings[name])) {
              $.extend(true, settings[name], value);
            } else {
              settings[name] = value;
            }
          } else {
            return settings[name];
          }
        },
        internal: function(name, value) {
          if ($.isPlainObject(name)) {
            $.extend(true, module, name);
          } else if (value !== undefined) {
            module[name] = value;
          } else {
            return module[name];
          }
        },
        debug: function() {
          if (!settings.silent && settings.debug) {
            if (settings.performance) {
              module.performance.log(arguments);
            } else {
              module.debug = Function.prototype.bind.call(
                console.info,
                console,
                settings.name + ":"
              );
              module.debug.apply(console, arguments);
            }
          }
        },
        verbose: function() {
          if (!settings.silent && settings.verbose && settings.debug) {
            if (settings.performance) {
              module.performance.log(arguments);
            } else {
              module.verbose = Function.prototype.bind.call(
                console.info,
                console,
                settings.name + ":"
              );
              module.verbose.apply(console, arguments);
            }
          }
        },
        error: function() {
          if (!settings.silent) {
            module.error = Function.prototype.bind.call(
              console.error,
              console,
              settings.name + ":"
            );
            module.error.apply(console, arguments);
          }
        },
        performance: {
          log: function(message) {
            var currentTime, executionTime, previousTime;
            if (settings.performance) {
              currentTime = new Date().getTime();
              previousTime = time || currentTime;
              executionTime = currentTime - previousTime;
              time = currentTime;
              performance.push({
                Name: message[0],
                Arguments: [].slice.call(message, 1) || "",
                Element: element,
                "Execution Time": executionTime
              });
            }
            clearTimeout(module.performance.timer);
            module.performance.timer = setTimeout(
              module.performance.display,
              500
            );
          },
          display: function() {
            var title = settings.name + ":",
              totalTime = 0;
            time = false;
            clearTimeout(module.performance.timer);
            $.each(performance, function(index, data) {
              totalTime += data["Execution Time"];
            });
            title += " " + totalTime + "ms";
            if (moduleSelector) {
              title += " '" + moduleSelector + "'";
            }
            if ($allModules.length > 1) {
              title += " " + "(" + $allModules.length + ")";
            }
            if (
              (console.group !== undefined || console.table !== undefined) &&
              performance.length > 0
            ) {
              console.groupCollapsed(title);
              if (console.table) {
                console.table(performance);
              } else {
                $.each(performance, function(index, data) {
                  console.log(
                    data["Name"] + ": " + data["Execution Time"] + "ms"
                  );
                });
              }
              console.groupEnd();
            }
            performance = [];
          }
        },
        invoke: function(query, passedArguments, context) {
          var object = instance,
            maxDepth,
            found,
            response;
          passedArguments = passedArguments || queryArguments;
          context = element || context;
          if (typeof query == "string" && object !== undefined) {
            query = query.split(/[\. ]/);
            maxDepth = query.length - 1;
            $.each(query, function(depth, value) {
              var camelCaseValue =
                depth != maxDepth
                  ? value +
                    query[depth + 1].charAt(0).toUpperCase() +
                    query[depth + 1].slice(1)
                  : query;
              if (
                $.isPlainObject(object[camelCaseValue]) &&
                depth != maxDepth
              ) {
                object = object[camelCaseValue];
              } else if (object[camelCaseValue] !== undefined) {
                found = object[camelCaseValue];
                return false;
              } else if ($.isPlainObject(object[value]) && depth != maxDepth) {
                object = object[value];
              } else if (object[value] !== undefined) {
                found = object[value];
                return false;
              } else {
                return false;
              }
            });
          }
          if ($.isFunction(found)) {
            response = found.apply(context, passedArguments);
          } else if (found !== undefined) {
            response = found;
          }
          if ($.isArray(returnedValue)) {
            returnedValue.push(response);
          } else if (returnedValue !== undefined) {
            returnedValue = [returnedValue, response];
          } else if (response !== undefined) {
            returnedValue = response;
          }
          return found;
        }
      };

      if (methodInvoked) {
        if (instance === undefined) {
          module.initialize();
        }
        module.invoke(query);
      } else {
        if (instance !== undefined) {
          instance.invoke("destroy");
        }
        module.initialize();
      }
    });

    return returnedValue !== undefined ? returnedValue : this;
  };

  $.fn.shape.settings = {
    // module info
    name: "Shape",

    // hide all debug content
    silent: false,

    // debug content outputted to console
    debug: false,

    // verbose debug output
    verbose: false,

    // fudge factor in pixels when swapping from 2d to 3d (can be useful to correct rounding errors)
    jitter: 0,

    // performance data output
    performance: true,

    // event namespace
    namespace: "shape",

    // width during animation, can be set to 'auto', initial', 'next' or pixel amount
    width: "initial",

    // height during animation, can be set to 'auto', 'initial', 'next' or pixel amount
    height: "initial",

    // callback occurs on side change
    beforeChange: function() {},
    onChange: function() {},

    // allow animation to same side
    allowRepeats: false,

    // animation duration
    duration: false,

    // possible errors
    error: {
      side: "You tried to switch to a side that does not exist.",
      method: "The method you called is not defined"
    },

    // classnames used
    className: {
      animating: "animating",
      hidden: "hidden",
      loading: "loading",
      active: "active"
    },

    // selectors used
    selector: {
      sides: ".sides",
      side: ".side"
    }
  };
})(jQuery, window, document);
